radeon: Firmware is required for DRM and KMS on R600 onward
authorBen Hutchings <ben@decadent.org.uk>
Tue, 8 Jan 2013 03:25:52 +0000 (03:25 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Thu, 17 Mar 2016 01:25:23 +0000 (01:25 +0000)
radeon requires firmware/microcode for the GPU in all chips, but for
newer chips (apparently R600 'Evergreen' onward) it also expects
firmware for the memory controller and other sub-blocks.

radeon attempts to gracefully fall back and disable some features if
the firmware is not available, but becomes unstable - the framebuffer
and/or system memory may be corrupted, or the display may stay black.
This does not seem to happen if KMS is disabled, but with both KMS
and GPU acceleration disabled radeon is not doing anything useful!

Therefore, perform a basic check for the existence of
/lib/firmware/radeon when a device is probed, and abort if it is
missing, except for the pre-R600 KMS case.

Gbp-Pq: Topic bugfix/all
Gbp-Pq: Name radeon-firmware-is-required-for-drm-and-kms-on-r600-onward.patch

drivers/gpu/drm/radeon/radeon_drv.c

index 5b6a6f5b3619e582afbb029279762d6bca5aa501..a108a3eb59ccda2c8eebd86850e55f2c52c10c89 100644 (file)
@@ -42,6 +42,8 @@
 
 #include "drm_crtc_helper.h"
 #include "radeon_kfd.h"
+#include <linux/namei.h>
+#include <linux/path.h>
 
 /*
  * KMS wrapper.
@@ -375,6 +377,42 @@ static struct drm_driver driver_old = {
 
 static struct drm_driver kms_driver;
 
+/* Test that /lib/firmware/radeon is a directory (or symlink to a
+ * directory).  We could try to match the udev search path, but let's
+ * assume people take the easy route and install
+ * firmware-linux-nonfree.
+ */
+static bool radeon_firmware_installed(void)
+{
+#if IS_BUILTIN(CONFIG_DRM_RADEON)
+       /* It may be too early to tell.  Assume it's there. */
+       return true;
+#else
+       struct path path;
+
+       if (kern_path("/lib/firmware/radeon", LOOKUP_DIRECTORY | LOOKUP_FOLLOW,
+                     &path) == 0) {
+               path_put(&path);
+               return true;
+       }
+
+       return false;
+#endif
+}
+
+#ifdef CONFIG_DRM_RADEON_UMS
+static int
+radeon_ums_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       if (!radeon_firmware_installed()) {
+               DRM_ERROR("radeon DRM requires firmware-linux-nonfree.\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+#endif
+
 static int radeon_kick_out_firmware_fb(struct pci_dev *pdev)
 {
        struct apertures_struct *ap;
@@ -401,6 +439,12 @@ static int radeon_pci_probe(struct pci_dev *pdev,
 {
        int ret;
 
+       if ((ent->driver_data & RADEON_FAMILY_MASK) >= CHIP_R600 &&
+           !radeon_firmware_installed()) {
+               DRM_ERROR("radeon kernel modesetting for R600 or later requires firmware-linux-nonfree.\n");
+               return -ENODEV;
+       }
+
        /* Get rid of things like offb */
        ret = radeon_kick_out_firmware_fb(pdev);
        if (ret)
@@ -623,6 +667,7 @@ static struct pci_driver *pdriver;
 static struct pci_driver radeon_pci_driver = {
        .name = DRIVER_NAME,
        .id_table = pciidlist,
+       .probe = radeon_ums_pci_probe,
 };
 #endif